package com.dbh.alg.algorithm.important;

/**
 * 给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串，则返回空字符串 "" 。
 * <p>
 *
 *
 * 注意：
 * <p>
 * 对于 t 中重复字符，我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。
 * 如果 s 中存在这样的子串，我们保证它是唯一的答案。
 * <p>
 *
 * 示例 1：
 * <p>
 * 输入：s = "ADOBECODEBANC", t = "ABC"
 * 输出："BANC"
 * 解释：最小覆盖子串 "BANC" 包含来自字符串 t 的 'A'、'B' 和 'C'。
 * 示例 2：
 * <p>
 * 输入：s = "a", t = "a"
 * 输出："a"
 * 解释：整个字符串 s 是最小覆盖子串。
 * 示例 3:
 * <p>
 * 输入: s = "a", t = "aa"
 * 输出: ""
 * 解释: t 中两个字符 'a' 均应包含在 s 的子串中，
 * 因此没有符合条件的子字符串，返回空字符串。
 * <p>
 * 【困难】
 */
public class Leetcode76_最小覆盖子串 {

    /**
     * 最小覆盖子串
     * @param s 主串
     * @param t 子串
     * @return 最小覆盖子串
     */
    public String minWindow(String s, String t) {
        if (s == null ||  t == null || s.length() < t.length()) {
            return "";
        }
        // 此处创建128长度的数组是因为题目中给出的字符串均为英文字母、大小写字母、数字和符号
        // 因此ASCII码范围在0-127之间
        int[] tables = new int[128];
        // 统计t中字符出现的次数
        for (char c : t.toCharArray()) {
            tables[c]++;
        }
        // 双指针滑动窗口
        // right指针不断右移扩大窗口，直到窗口中包含t中所有字符
        // 然后left指针右移缩小窗口，直到窗口中不再包含t中所有字符
        // 每次窗口包含t中所有字符时，更新最小覆盖子串
        int left = 0, right = 0, count = 0, minLength = Integer.MAX_VALUE, start = 0;
        while (right < s.length()) {
            // right指针右移，扩大窗口
            char c = s.charAt(right);
            // 如果当前字符在t中出现过，且窗口中该字符数量小于等于t中该字符数量，则count加1
            if (tables[c] > 0) {
                count++;
            }
            // 无论当前字符是否在t中出现过，窗口中该字符数量都要减1
            tables[c]--;
            // 右移right指针
            right++;
            // 当窗口中包含t中所有字符时，开始移动left指针，缩小窗口
            while (count == t.length()) {
                // 更新最小覆盖子串
                if (right - left < minLength) {
                    minLength = right - left;
                    start = left;
                }
                // left指针右移，缩小窗口
                char cLeft = s.charAt(left);
                // 无论当前字符是否在t中出现过，窗口中该字符数量都要加1
                tables[cLeft]++;
                // 如果当前字符在t中出现过，且窗口中该字符数量大于t中该字符数量，则count减1
                if (tables[cLeft] > 0) {
                    count--;
                }
                left++;
            }
        }
        return minLength == Integer.MAX_VALUE ? "" : s.substring(start, start + minLength);
    }
}
